package com.itheima.d8_thread_pool;

import java.util.concurrent.*;

/**
 * 目标：掌握线程池的创建。
 *      线程六大状态之间的转换在day11的PPT，第77张
 *
 * 正在运行的程序（软件）就是一个独立的进程。
 * 线程是属于进程的，一个进程中可以同时运行很多个线程。
 * 进程中的多个线程其实是并发和并行执行的。
 *
 * 并发的含义
 * 进程中的线程是由CPU负责调度执行的，但CPU能同时处理线程的数量有限，为了保证全部线程都能往前执行，
 * CPU会轮询为系统的每个线程服务，由于CPU切换的速度很快，给我们的感觉这些线程在同时执行，这就是并发。
 *
 * 并行的理解
 * 在同一个时刻上，同时有多个线程在被CPU调度执行。
 *
 * 简单说说多线程是怎么执行的？
 * 并发：CPU分时轮询的执行线程。
 * 并行：同一个时刻同时在执行。
 *
 * 线程的生命周期
 * 也就是线程从生到死的过程中，经历的各种状态及状态转换。
 * 理解线程这些状态有利于提升并发编程的理解能力。
 * Java总共定义了6种状态
 * 6种状态都定义在Thread类的内部枚举类中。
 * 线程的6种状态互相转换
 *
 * 线程的6种状态总结
 * NEW,新建                    线程刚被创建，但是并未启动。
 * RUNNABLE,可运行             线程已经调用了start()，等待CPU调度
 * BLOCKED,锁阻塞              线程在执行的时候未竞争到锁对象，则该线程进入Blocked状态；
 * WAITING,无限等待            一个线程进入Waiting状态，另一个线程调用notify或者notifyAll方法才能够唤醒
 * TIMED_WAITING,计时等待      同waiting状态，有几个方法（sleep,wait）有超时参数，调用他们将进入Timed Waiting状态。
 * TERMINATED;被终止          因为run方法正常退出而死亡，或者因为没有捕获的异常终止了run方法而死亡。
 *
 *      线程调用wait方法会释放锁资源也就是锁对象，wait时间到或者wait时间没到但是被其他线程notify，
 *              也需要去获取锁对象，没获取到也会进去blocked阻塞态
 *      线程调用sleep是不会释放锁对象的，假设小明线程抢了锁之后进去调用sleep方法在里面睡觉，
 *              sleep时间一到会立马从TIMED_WAITING状态进入RUNNABLE状态
 *
 *
 * 多线程的第三种创建方式：利用Callable接口、FutureTask类来实现。
 *  1、创建任务对象
 *          定义一个类实现Callable接口，重写call方法，封装要做的事情，和要返回的数据。
 *          把Callable类型的对象封装成FutureTask（线程任务对象）。
 * 2、把线程任务对象交给Thread对象。
 * 3、调用Thread对象的start方法启动线程。
 * 4、线程执行完毕后、通过FutureTask对象的的get方法去获取线程任务执行的结果。
 *
 * 线程的创建方式一:继承Thread类 ，多线程的创建方式二：实现Runnable接口 这两种创建方式都存在的问题：
 *       假如线程执行完毕后有一些数据需要返回，他们重写的run方法均不能直接返回结果。
 * 怎么解决：
 *      JDK 5.0提供了Callable接口和FutureTask类来实现（多线程的第三种创建方式）。
 *      这种方式最大的优点：可以返回线程执行完毕后的结果。
 *
 * FutureTask的API
 *          FutureTask提供的构造器
 * public FutureTask<>(Callable call)       把Callable对象封装成FutureTask对象。
 *          FutureTask提供的方法
 * public V get() throws Exception          获取线程执行call方法返回的结果。
 *
 *
 * 弹幕：
 * Runnable 和 Callable没有创建线程，实现这两个接口标记的类的对象是线程要处理的任务，
 * 继承了Thread的类才是创建了线程，有Run方法的线程实现类。
 * runnable和callable其实是任务
 */
public class ThreadPoolTest2 {
    public static void main(String[] args) throws Exception {
        // 1、通过ThreadPoolExecutor创建一个线程池对象。
        ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
                TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());

        // 2、使用线程处理Callable任务。ExecutorService的常用方法:
/*        Future<T> submit(Callable<T> task):
         会返回一个未来任务对象也就是future对象给我们，执行 Callable 任务，返回未来任务对象，用于获取线程返回的结果
        接着用这个future对象去获取我们线程池处理完这个任务后返回的结果

        把这个callable对象交给线程池，线程池接着会创建线程去处理这个callable任务，调用的还是这个callable任务里面的call方法执行的
        执行完之后，会返回一个未来任务对象给我们，然后通过未来任务对象去调用get方法获取这个线程执行完毕后的这个任务的结果
        */
        //Future是FutureTask的父类形式，Future<String>返回类型是string，因为callable里面的call方法返回类型是string
        Future<String> f1 = pool.submit(new MyCallable(100));
        Future<String> f2 = pool.submit(new MyCallable(200));
        Future<String> f3 = pool.submit(new MyCallable(300));

        //核心线程就三个，核心线程满了后，就会复用前面使用完的核心线程
        //弹幕：这里并没有创建线程哦，只是创建了任务对象放线程里了。callerble对比runnable是可以获取返回值
        Future<String> f4 = pool.submit(new MyCallable(400));

        System.out.println(f1.get());       //有异常直接抛出
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
    }
}
